﻿using System;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Drawing;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using Table_pet;

namespace MyDesktopPetDemo
{
	public class FoodGame
	{
		public int Score { get; private set; } = 0;
		List<Food> foods = new();
		public System.Timers.Timer Timer = new();
		public float CatPosX { get; set; } = 0f;
		float countDownFloat = gameTime;
		int CountDown
		{
			get => (int)Math.Ceiling(countDownFloat);
		}
		float foodGenerateTimer = 1f;
		float bombGenerateTimer = 5f;
		float bombAnimTimer = 0f;
		float scoreAnimTimer = 0f;
		float completeTimer = 0f;
		bool running;

		const int gameTime = 60;
		const int timerIntervalMs = 16;
		const float deltaTime = timerIntervalMs / 1000f;
		const float foodSpeed = 0.63f;
		const float bombSpeed = 0.9f;
		const float foodAccel = 1.5f;
		const float foodJudgeRange = 0.1f;
		const float bombJudgeRange = 0.05f;
		const float catWidth = 0.2f;
		const float foodRadius = 24f;
		const float generateInterval = 0.4f;
		const float generatePosY = -0.05f;
		const float judgePosY = 0.8f;
		const int eatReward = 1;
		const int missPenalty = 3;
		const int bombPenalty = 10;
		const float generateBorderWidth = 0.1f;
		const float generatePosMaxSpan = 1f;
		const float bombAnimTime = 1f;
		const float scoreAnimTime = 0.08f;

		public delegate void GameCompleteHandler(int score);
		public event GameCompleteHandler GameComplete = _ => { };

		readonly Image catImage;
		readonly Image bombImage;
		readonly Image foodImage;

		public class Food
		{
			public bool IsBomb { get; set; }
			public Vector2 Pos { get; set; }
			public float Time { get; set; } = 0f;

			public Food(float posX, bool isBomb = false)
			{
				Pos = new Vector2(posX, generatePosY);
				IsBomb = isBomb;
			}
		}

		/*public class Particle
		{
			public Vector2 Pos { get; set; }
			public Vector2 Vel { get; set; }

		}*/

		public FoodGame()
		{
			catImage = Table_pet.Properties.Resources.FoodGame_Cat;
			//catImage = Image.FromFile(Application.StartupPath + "/Resources/FoodGame_Cat.gif");
			catImage.RotateFlip(RotateFlipType.Rotate270FlipNone);
			bombImage = Table_pet.Properties.Resources.FoodGame_Bomb;
			//bombImage = Image.FromFile(Application.StartupPath + "/Resources/FoodGame_Bomb.png");
			foodImage = Table_pet.Properties.Resources.FoodGame_Food;
			//foodImage = Image.FromFile(Application.StartupPath + "/Resources/FoodGame_Food.png");
		}

		public void Start()
		{
			Timer.Interval = timerIntervalMs;
			Timer.Elapsed += Update;
			Timer.Start();
			running = true;
		}

		void Terminate()
		{
			//Timer.Stop();
			running = false;

			// GameComplete(Score);
		}

		public void Update(object? sender, ElapsedEventArgs e)
		{
			if ((bombAnimTimer -= deltaTime) < 0f) bombAnimTimer = 0f;
			if ((scoreAnimTimer -= deltaTime) < 0f) scoreAnimTimer = 0f;
			if (!running)
			{
				completeTimer -= deltaTime;
				if (completeTimer <= -2f && completeTimer > -3f)
                {
					completeTimer = -5f;
                    if (Timer.Enabled) GameComplete(Score);
                    Timer.Stop();
                }
			}

			if (!running) return;

			if ((countDownFloat -= deltaTime) < 0f || Score < 0)
			{
				if (countDownFloat < 0f) countDownFloat = 0f;
				if (Score < 0) Score = 0;
				Terminate();
			}

			Random random = new();
			float t = countDownFloat / gameTime;
			while ((foodGenerateTimer -= deltaTime) < 0f)
			{
				Generate();
				foodGenerateTimer += generateInterval * (
					bombAnimTimer > 0f
					?
					(float)random.NextDouble() * 0.3f + 0.2f * (1f - Math.Min(bombAnimTimer / bombAnimTime, 1.5f) * 0.5f)
					:
					((0.8f - 0.6f * (1f - t)) + (float)random.NextDouble() * (0.2f + 0.6f * (1f - t)))
					*
					((float)Math.Pow(t, 2) * 2.2f + 0.8f)
				);
			}
			while ((bombGenerateTimer -= deltaTime) < 0f)
			{
				if (random.NextDouble() < 0.3 * (1 - t) + 0.05)
				{
					Generate(isBomb: true);
				}
				bombGenerateTimer += generateInterval * (t * 0.4f + 0.6f);
			}
			List<Food> missedFoods = new(), eatenFoods = new();
			lock (foods)
			{
				foreach (Food food in foods)
				{
					float judgeRange = food.IsBomb ? bombJudgeRange : foodJudgeRange;
					//food.pos += new Vector2(0f, ((float)Math.Sqrt(food.pos.Y - generatePosY) * 15f + 1f) * foodSpeed * deltaTime);
					food.Pos += new Vector2(0f, (food.IsBomb ? bombSpeed : foodSpeed) * (1f + (float)Math.Sqrt(food.Pos.Y - generatePosY) * foodAccel) * (1f + 0.4f * bombAnimTimer / bombAnimTime) * deltaTime);
					if (food.Pos.Y > 1f)//- judgePosY > judgeRange * 0.5f)
					{
						if (!food.IsBomb)
						{
							missedFoods.Add(food);
						}
					}
					if (Math.Abs(food.Pos.X - CatPosX) <= catWidth * 0.5f && Math.Abs(food.Pos.Y - judgePosY) <= judgeRange * 0.5f)
					{
						if (food.IsBomb)
						{
							missedFoods.Add(food);
							Explode();
						}
						else
						{
							eatenFoods.Add(food);
						}
					}
				}
				foreach (Food food in missedFoods) Miss(food);
				foreach (Food food in eatenFoods) Eat(food);
			}
		}

		float? lastGeneratePosX = null;
		void Generate(bool isBomb = false)
		{
			float posX;
			/*if (lastGeneratePosX == null)
			{
				posX = (float)new Random().NextDouble() * (1f - generateBorderWidth * 2f) + generateBorderWidth;
			}
			else
			{
				posX = lastGeneratePosX.Value + (float)new Random().NextDouble() * generatePosMaxSpan - generatePosMaxSpan * 0.5f;
				if (posX < generateBorderWidth) posX = generateBorderWidth;
				if (posX > 1f - generateBorderWidth) posX = 1f - generateBorderWidth;
			}*/
			posX = (float)new Random().NextDouble() * (1f - generateBorderWidth * 2f) + generateBorderWidth;
			foods.Add(new Food(posX, isBomb));
			lastGeneratePosX = posX;
		}

		void Explode()
		{
			bombAnimTimer += bombAnimTime;
			Score -= bombPenalty;
		}

		void Miss(Food food)
		{
			foods.Remove(food);
			Score -= missPenalty;
		}

		void Eat(Food food)
		{
			foods.Remove(food);
			Score += eatReward;
			scoreAnimTimer += scoreAnimTime;
		}

		public void Render(Graphics gr, PictureBox pic)
		{
			float angle = 0f;
			float explosionAnimT = bombAnimTimer / bombAnimTime;
			angle += 2f * (float)Math.Sin(explosionAnimT * Math.PI * 32f) * (float)Math.Pow(explosionAnimT, 2);
			Matrix transformMatrix = new();
			transformMatrix.RotateAt(angle, new(pic.Width * 0.5f, pic.Height * 0.5f));
			gr.Transform = transformMatrix;

			Brush brush1 = new SolidBrush(Color.White);
			float scoreAnimScale = 1f + 0.04f * (float)Math.Pow(Math.Sin(scoreAnimTimer / scoreAnimTime * Math.PI), 1);
			float scoreAnimOffset = (scoreAnimScale - 1f) * -60f;
			gr.DrawString($"倒计时：{Math.Max(CountDown, 0)}", new Font("微软雅黑", 20f), brush1, 24f, 16f);
			gr.DrawString($"吃到了：{Math.Max(Score, 0)}", new Font("微软雅黑", 20f * scoreAnimScale), brush1, 24f + scoreAnimOffset, 64f + scoreAnimOffset);

			if (!running)
			{
				gr.DrawString(CountDown <= 0 ? "成功了！" : "失败了！", new Font("微软雅黑", 50f), brush1, 24f, 128f);
				return;
			}

			Pen pen1 = new(new SolidBrush(Color.Tan), 14f);
			Pen pen2 = new(new SolidBrush(Color.Tan), 48f);
			Brush brush2 = new SolidBrush(Color.Orange);
			Brush brush3 = new SolidBrush(Color.DarkRed);
			Brush brush4 = new SolidBrush(Color.White);
			//gr.DrawLine(pen2, (CatPosX - catWidth * 0.3f) * pic.Width, judgePosY * pic.Height + 24f, (CatPosX + catWidth * 0.3f) * pic.Width, judgePosY * pic.Height + 24f);
			//gr.DrawLine(pen1, (CatPosX - catWidth * 0.4f) * pic.Width, judgePosY * pic.Height, (CatPosX + catWidth * 0.4f) * pic.Width, judgePosY * pic.Height);
			float catImageWidth = catWidth * 0.85f * pic.Width;
			float catImageHeight = catImageWidth * catImage.Height / catImage.Width;
			gr.DrawImage(catImage, CatPosX * pic.Width - catImageWidth * 0.5f, judgePosY * pic.Height - catImageHeight * 0.5f, catImageWidth, catImageHeight);
			lock (foods)
			{
				foreach (var food in foods)
				{
					food.Time += deltaTime;
					if (!food.IsBomb)
					{
						//gr.FillEllipse(brush2, food.Pos.X * pic.Width - foodRadius, food.Pos.Y * pic.Height - foodRadius, 2f * foodRadius, 2f * foodRadius);
						float foodImageWidth = foodRadius * 2f;
						float foodImageHeight = foodImageWidth * foodImage.Height / foodImage.Width;
						gr.DrawImage(foodImage, food.Pos.X * pic.Width - foodImageWidth * 0.5f, food.Pos.Y * pic.Height - foodImageHeight * 0.5f, foodImageWidth, foodImageHeight);
					}
					else
					{
						//gr.FillRectangle(brush3, food.Pos.X * pic.Width - foodRadius * 1.5f, food.Pos.Y * pic.Height - foodRadius * 1.5f, 3f * foodRadius, 3f * foodRadius);
						float bombImageWidth = foodRadius * 3f;
						float bombImageHeight = bombImageWidth * bombImage.Height / bombImage.Width;
						bool white = (int)(food.Time / 0.2f) % 2 == 1;
						if (white)
						{
							gr.FillRectangle(brush4, food.Pos.X * pic.Width - bombImageWidth * 0.5f, food.Pos.Y * pic.Height - bombImageHeight * 0.5f, bombImageWidth, bombImageHeight);
						}
						else
						{
							gr.DrawImage(bombImage, food.Pos.X * pic.Width - bombImageWidth * 0.5f, food.Pos.Y * pic.Height - bombImageHeight * 0.5f, bombImageWidth, bombImageHeight);
						}
					}
				}
			}
		}
	}

}
